// SPDX-License-Identifier: Mulan PSL v2
/*
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * This software is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

use std::{error::Error as StdError, marker::PhantomData};

use thiserror::Error;

use super::Framer;

const FRAME_MAGIC_NUMBER: u32 = 0x78464D45; // 'xFME' in ASCII

const FRAME_MAGIC_SIZE: usize = std::mem::size_of::<u32>();
const FRAME_LENGTH_SIZE: usize = std::mem::size_of::<u32>();
const FRAME_CHECKSUM_SIZE: usize = std::mem::size_of::<u32>();

const FRAME_HEADER_SIZE: usize = FRAME_MAGIC_SIZE + FRAME_LENGTH_SIZE + FRAME_CHECKSUM_SIZE;

const FRAME_MAGIC_START: usize = 0;
const FRAME_MAGIC_END: usize = FRAME_MAGIC_START + FRAME_MAGIC_SIZE;

const FRAME_LENGTH_START: usize = FRAME_MAGIC_END;
const FRAME_LENGTH_END: usize = FRAME_LENGTH_START + FRAME_LENGTH_SIZE;

const FRAME_CHECKSUM_START: usize = FRAME_LENGTH_END;
const FRAME_CHECKSUM_END: usize = FRAME_CHECKSUM_START + FRAME_CHECKSUM_SIZE;

const DEFAULT_MAX_FRAME_LEN: usize = 16 * 1024 * 1024;

/// Errors that can occur by `GenericFramer`.
#[derive(Debug, Error)]
pub enum GenericFramerError {
    #[error("insufficient buffer capacity (required: {required}, capacity: {capacity})")]
    InsufficientBuffer { required: usize, capacity: usize },

    #[error("failed to write frame payload")]
    WritePayload(#[source] Box<dyn StdError + Send + Sync + 'static>),

    #[error("frame length exceeds limit (max: {max} bytes, actual: {actual} bytes)")]
    FrameTooLarge { max: usize, actual: usize },

    #[error("invalid magic number (expected: {expected:#010X}, actual: {actual:#010X})")]
    InvalidMagicNumber { expected: u32, actual: u32 },

    #[error("frame checksum mismatch (expected: {expected:#010X}, actual: {actual:#010X})")]
    InvalidChecksum { expected: u32, actual: u32 },
}

/// A Framer that prefixes each payload with a magic number, its length (u32), and a CRC32 checksum.
///
/// This implementation provides a robust and high-performance framing mechanism suitable for
/// network protocols and binary data streams.
///
/// # Frame Layout
///
/// ```text
/// +--------------+-------------------+-------------------+-------------------+
/// | Magic Number |  Payload Length   |     Checksum      |      Payload      |
/// | (u32, fixed) | (u32, Big Endian) | (u32, Big Endian) |  (variable size)  |
/// +--------------+-------------------+-------------------+-------------------+
/// |                     Frame Header                     |                   |
/// +--------------------------------------------------------------------------+
/// ```
///
/// - **Magic Number**: A constant value (`0x78464D45`) to identify the start of a frame.
/// - **Payload Length**: The length of the `Payload` field in bytes.
/// - **Checksum**: A CRC32 checksum calculated over the `Payload Length` and `Payload` fields.
#[derive(Debug, Clone, Copy)]
pub struct GenericFramer {
    max_len: usize,
}

impl GenericFramer {
    /// Creates a new instance of `GenericFramer` with a specified maximum frame length.
    ///
    /// # Panics
    ///
    /// Panics if `max_len` is smaller than the frame header size (`FRAME_HEADER_SIZE`),
    /// or if `max_len` exceeds the maximum value representable by a `u32`.
    pub fn new(max_len: usize) -> Self {
        assert!(
            max_len >= FRAME_HEADER_SIZE,
            "max_len ({}) cannot be smaller than the frame header size ({})",
            max_len,
            FRAME_HEADER_SIZE
        );
        assert!(
            max_len <= (u32::MAX as usize),
            "max_len ({}) exceeds the maximum encodable size for a u32 length field ({})",
            max_len,
            u32::MAX
        );
        Self { max_len }
    }
}

impl Default for GenericFramer {
    fn default() -> Self {
        Self::new(DEFAULT_MAX_FRAME_LEN)
    }
}

impl Framer for GenericFramer {
    type Error = GenericFramerError;

    fn header_len(&self) -> usize {
        FRAME_HEADER_SIZE
    }

    fn prepare_buffer<'a>(
        &self,
        buff: &'a mut [u8],
    ) -> Result<(&'a mut [u8], &'a mut [u8]), Self::Error> {
        let header_len = self.header_len();
        let buff_len = buff.len();

        if buff_len < header_len {
            return Err(GenericFramerError::InsufficientBuffer {
                required: header_len,
                capacity: buff_len,
            });
        }

        Ok(buff.split_at_mut(header_len))
    }

    fn encode_frame<'a>(
        &self,
        header: &'a mut [u8],
        payload: &'a [u8],
    ) -> Result<usize, Self::Error> {
        let payload_len = payload.len();

        let frame_len = FRAME_HEADER_SIZE + payload_len;
        if frame_len > self.max_len {
            return Err(GenericFramerError::FrameTooLarge {
                max: self.max_len,
                actual: frame_len,
            });
        }

        /* Write magic number */
        let magic_buff = &mut header[FRAME_MAGIC_START..FRAME_MAGIC_END];
        magic_buff.copy_from_slice(&FRAME_MAGIC_NUMBER.to_be_bytes());

        /* Write payload length */
        let length_buff = &mut header[FRAME_LENGTH_START..FRAME_LENGTH_END];
        length_buff.copy_from_slice(&(payload_len as u32).to_be_bytes());

        /* Calculate checksum (over length and payload) */
        let mut crc32_hasher = crc32fast::Hasher::new();
        crc32_hasher.update(length_buff);
        crc32_hasher.update(payload);

        let checksum = crc32_hasher.finalize();

        /* Write checksum */
        let checksum_buff = &mut header[FRAME_CHECKSUM_START..FRAME_CHECKSUM_END];
        checksum_buff.copy_from_slice(&checksum.to_be_bytes());

        Ok(frame_len)
    }

    fn decode_frame<'a>(&self, buff: &'a [u8]) -> Result<Option<(usize, &'a [u8])>, Self::Error> {
        if buff.len() < FRAME_HEADER_SIZE {
            return Ok(None); // Not enough data for a full header
        }

        let magic_slice = &buff[FRAME_MAGIC_START..FRAME_MAGIC_END];
        let length_slice = &buff[FRAME_LENGTH_START..FRAME_LENGTH_END];
        let checksum_slice = &buff[FRAME_CHECKSUM_START..FRAME_CHECKSUM_END];

        /* Read frame header */
        let magic = u32::from_be_bytes(magic_slice.try_into().unwrap());
        let payload_len = u32::from_be_bytes(length_slice.try_into().unwrap()) as usize;
        let checksum = u32::from_be_bytes(checksum_slice.try_into().unwrap());

        /* Check magic number */
        if magic != FRAME_MAGIC_NUMBER {
            return Err(GenericFramerError::InvalidMagicNumber {
                expected: FRAME_MAGIC_NUMBER,
                actual: magic,
            });
        }

        /* Check frame size against limit */
        let frame_len = FRAME_HEADER_SIZE + payload_len;
        if frame_len > self.max_len {
            return Err(GenericFramerError::FrameTooLarge {
                max: self.max_len,
                actual: frame_len,
            });
        }

        /* Check if the full frame is available in the buffer */
        if buff.len() < frame_len {
            return Ok(None); // Not enough data for the full frame
        }

        /* Calculate checksum */
        let payload_slice = &buff[FRAME_HEADER_SIZE..frame_len];

        let mut crc32_hasher = crc32fast::Hasher::new();
        crc32_hasher.update(length_slice);
        crc32_hasher.update(payload_slice);

        /* Verify checksum */
        let actual_checksum = crc32_hasher.finalize();
        if actual_checksum != checksum {
            return Err(GenericFramerError::InvalidChecksum {
                expected: checksum,
                actual: actual_checksum,
            });
        }

        Ok(Some((frame_len, payload_slice)))
    }
}
